/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2024-2024. All rights reserved.
 * Description: 集合通信NPU侧标准notify
 */

#include "rts_notify.h"
#include "externalinput_pub.h"
#include "sal_pub.h"
#include "device_capacity.h"

namespace hccl {

RtsNotify::RtsNotify(NotifyType notifyType)
    : NotifyBase(notifyType)
{
}

RtsNotify::RtsNotify(NotifyType notifyType, HcclNotifyInfo notifyInfo)
    : NotifyBase(notifyType, notifyInfo)
{
}

RtsNotify::RtsNotify(NotifyType notifyType, const HcclSignalInfo &notifyInfo)
    : NotifyBase(notifyType)
{
    (void)SetNotifyData(notifyInfo);
}

RtsNotify::~RtsNotify()
{
    (void)Destroy();
}

HcclResult RtsNotify::Open()
{
    HCCL_DEBUG("[RtsNotify][Open]remote withIpc[%d], notify type[%d], ipcName[%s].",
        notifyInfo_.ipcNotify.withIpc, notifyType, notifyInfo_.ipcNotify.ipcName);
    if (notifyInfo_.ipcNotify.withIpc) {
        if (notifyType == NotifyType::RUNTIME_NOTIFY) {
            CHK_RET(hrtIpcOpenNotify(&notifyPtr, notifyInfo_.ipcNotify.ipcName));
        } else {
            CHK_RET(hrtIpcOpenNotifyWithFlag(&notifyPtr, notifyInfo_.ipcNotify.ipcName,
                RT_NOTIFY_FLAG_DOWNLOAD_TO_DEV));
        }
    } else {
        notifyPtr = notifyInfo_.ipcNotify.ptr;
    }
    HCCL_DEBUG("[RtsNotify][Open]notifyPtr[%p], ipcNotify[%p].", notifyPtr, notifyInfo_.ipcNotify.ptr);

    CHK_PRT_RET(notifyPtr == nullptr, HCCL_ERROR("[RtsNotify][Open]errNo[0x%016llx] Notify open failed. "\
        "notify is nullptr", HCCL_ERROR_CODE(HCCL_E_RUNTIME)), HCCL_E_RUNTIME);

    inchip = false;
    isLocal = false;
    CHK_RET(UpdateNotifyInfo());
    CHK_RET(hrtNotifyGetAddr(notifyPtr, &address));

    return HCCL_SUCCESS;
}

HcclResult RtsNotify::Close()
{
    return Destroy();
}

HcclResult RtsNotify::Wait(Stream& stream, HcclDispatcher dispatcher, s32 stage, u32 timeOut)
{
    CHK_PTR_NULL(dispatcher);
    return reinterpret_cast<DispatcherPub*>(dispatcher)->SignalWait(
        notifyPtr, stream, INVALID_VALUE_RANKID, INVALID_VALUE_RANKID, stage, inchip, INVALID_UINT, timeOut);
}

HcclResult RtsNotify::Post(Stream& stream, HcclDispatcher dispatcher, s32 stage)
{
    CHK_PTR_NULL(dispatcher);
    return reinterpret_cast<DispatcherPub*>(dispatcher)->SignalRecord(
        notifyPtr, stream, INVALID_VALUE_RANKID, notifyInfo_.ipcNotify.offset, stage, inchip, address);
}

HcclResult RtsNotify::Wait(Stream& stream, HcclDispatcher dispatcher, s32 stage, u32 timeOut,
    u32 userRank, u32 remoteUserRank)
{
    CHK_PTR_NULL(dispatcher);
    return reinterpret_cast<DispatcherPub*>(dispatcher)->SignalWait(
        notifyPtr, stream, userRank, remoteUserRank, stage, inchip, INVALID_UINT, timeOut);
}

HcclResult RtsNotify::Post(Stream& stream, HcclDispatcher dispatcher, s32 stage, u32 remoteUserRank)
{
    CHK_PTR_NULL(dispatcher);
    return reinterpret_cast<DispatcherPub*>(dispatcher)->SignalRecord(
        notifyPtr, stream, remoteUserRank, notifyInfo_.ipcNotify.offset, stage, inchip, address);
}

HcclResult RtsNotify::SetIpc()
{
    SecIpcName_t ipcName;
    HcclResult ret = hrtIpcSetNotifyName(notifyPtr, reinterpret_cast<u8 *>(ipcName.ipcName),
        sizeof(ipcName.ipcName));
    CHK_PRT_RET(ret != HCCL_SUCCESS, HCCL_ERROR("[SetIpc][hrtIpcSetNotifyName]errNo[0x%016llx]  "\
        " IPC set notify name fail. return[%d] name len=[%zu].", HCCL_ERROR_CODE(HCCL_E_RUNTIME),\
        ret, sizeof(ipcName.ipcName)), HCCL_E_RUNTIME);
    if (memcpy_s(notifyInfo_.ipcNotify.ipcName, HCCL_IPC_MEM_NAME_LEN,
        reinterpret_cast<char *>(ipcName.ipcName), sizeof(ipcName.ipcName)) != EOK) {
        HCCL_ERROR("ipcName:%s, size:%u", ipcName.ipcName, sizeof(ipcName.ipcName));
        return HCCL_E_MEMORY;
    };
    HCCL_DEBUG("[RtsNotify][SetIpc]ipcName:%s, size:%u.", ipcName.ipcName, sizeof(ipcName.ipcName));
    CHK_RET(hrtNotifyGetAddr(notifyPtr, &address));
    CHK_RET(UpdateNotifyInfo());

    return HCCL_SUCCESS;
}

HcclResult RtsNotify::Grant(s64 recvId)
{
    // 设置notify 的白名单
    inchip = false;
    s32 pid  = static_cast<s32>((recvId & 0x00000000FFFFFFFF));
    u32 localPid = 0;
    CHK_RET(SalGetBareTgid(reinterpret_cast<u32*>(&localPid))); // 当前进程id

    // 多进程操作多卡场景，notify pool用pid区分notify
    s32 sdid = static_cast<s32>((recvId & 0xFFFFFFFF00000000) >> 32);
    HCCL_DEBUG("[RtsNotify][Grant]remote sdid[%016llx], remote pid[%d], local pid[%u], withIpc[%d].",
        sdid, pid, localPid, notifyInfo_.ipcNotify.withIpc);

    // 单进程多线程操作多卡场景，notify pool用rankId区分notify
    if (static_cast<u32>(pid) == localPid && sdid == INVALID_INT) {
        if (notifyType == NotifyType::RUNTIME_NOTIFY_MC2) {
            notifyInfo_.ipcNotify.withIpc = true;
        }
        return HCCL_SUCCESS;
    }

    notifyInfo_.ipcNotify.withIpc = true;

    if (sdid != INVALID_INT) {
        // recvId由s32的sdid和pid拼接而成, 高32位是sdid, 低32位是pid
        CHK_RET(hrtSetIpcNotifySuperPodPid(notifyInfo_.ipcNotify.ipcName, sdid, &pid, IPC_NOTIFY_PID_ARRAY_SIZE));
    } else {
        CHK_RET(hrtSetIpcNotifyPid(notifyInfo_.ipcNotify.ipcName, &pid, IPC_NOTIFY_PID_ARRAY_SIZE));
    }
    return HCCL_SUCCESS;
}

HcclResult RtsNotify::Alloc()
{
    s32 deviceId = 0;
    CHK_RET(hrtGetDevice(&deviceId));

    if (notifyType == NotifyType::RUNTIME_NOTIFY) {
        CHK_RET(hrtNotifyCreate(deviceId, &notifyPtr));
    } else {
        CHK_RET(hrtNotifyCreateWithFlag(deviceId, &notifyPtr));
    }
    CHK_PRT_RET(notifyPtr == nullptr, HCCL_ERROR("[RtsNotify][Alloc]errNo[0x%016llx] Notify create failed. "\
        "notify is nullptr", HCCL_ERROR_CODE(HCCL_E_RUNTIME)), HCCL_E_RUNTIME);
    if (notifyType == NotifyType::RUNTIME_NOTIFY_MC2) {
        CHK_RET(UpdateNotifyInfo());
    }
    CHK_RET(hrtNotifyGetOffset(notifyPtr, notifyInfo_.ipcNotify.offset));
    notifyInfo_.ipcNotify.ptr = notifyPtr;

    return HCCL_SUCCESS;
}

HcclResult RtsNotify::Destroy()
{
    // 本卡notify直接释放，非本卡判断且非单进程多线程场景直接释放
    if (notifyPtr != nullptr && (isLocal || notifyInfo_.ipcNotify.withIpc)) {
        CHK_RET(hrtNotifyDestroy(notifyPtr));
    }
    notifyPtr = nullptr;
    return HCCL_SUCCESS;
}

HcclResult RtsNotify::UpdateNotifyInfo()
{
    CHK_RET(hrtGetNotifyID(notifyPtr, &id));

    CHK_RET(hrtNotifyGetPhyInfo(notifyPtr, &devId, &tsId));

    rtNotifyPhyInfo notifyInfo;
    CHK_RET(hrtNotifyGetPhyInfoExt(notifyPtr, &notifyInfo));
    flag = notifyInfo.flag;

    return HCCL_SUCCESS;
}
}